import { Injectable, Logger } from '@nestjs/common';
import * as ExcelJS from 'exceljs';

/**
 * 工作表配置接口
 */
interface SheetConfig {
  name: string;
  columns: Partial<ExcelJS.Column>[];
  data: any[];
}

@Injectable()
export class ExcelService {
  private readonly logger = new Logger(ExcelService.name);

	/**
	 * 创建工作簿
	 * @description 创建工作簿
	 * @param sheetName 工作表名称
	 * @param columns 列定义
	 * @param data 数据
	 * @returns 工作簿
	 */
  async createWorkbook(
    sheetName: string,
    columns: Partial<ExcelJS.Column>[],
    data: any[]
  ): Promise<ExcelJS.Workbook> {
    this.logger.log(`开始创建工作簿 - 工作表名称: ${sheetName}`);
    this.logger.debug(`列定义数量: ${columns.length}, 数据行数: ${data.length}`);

    try {
      const workbook = new ExcelJS.Workbook();
      this.logger.debug('工作簿实例创建成功');

      const worksheet = workbook.addWorksheet(sheetName);
      this.logger.debug(`工作表 "${sheetName}" 添加成功`);

      worksheet.columns = columns;
      this.logger.debug('列定义设置完成');

      worksheet.addRows(data);
      this.logger.debug(`数据行添加完成，共 ${data.length} 行`);

      this.logger.log(`工作簿创建成功 - 工作表: ${sheetName}, 数据行数: ${data.length}`);
      return workbook;
    } catch (error) {
      this.logger.error(`创建工作簿失败 - 工作表: ${sheetName}`, error);
      throw error;
    }
  }

  /**
   * 导出Excel文件
   * @param sheetConfigs 工作表配置数组
   * @param filePath 可选，文件路径，如果不提供则返回Buffer
   * @returns 如果提供filePath则返回void，否则返回Buffer
   */
  async export(
    sheetConfigs: SheetConfig[] | SheetConfig,
    filePath?: string
  ): Promise<ExcelJS.Buffer | void> {
    this.logger.log('开始导出Excel文件');

    try {
      // 处理单个工作表配置的情况
      const configs = Array.isArray(sheetConfigs) ? sheetConfigs : [sheetConfigs];
      this.logger.debug(`准备导出 ${configs.length} 个工作表`);

      const workbook = await this.createMultiSheetWorkbook(configs);

      if (filePath) {
        await this.writeToFile(workbook, filePath);
        this.logger.log(`Excel文件导出成功，保存至: ${filePath}`);
        return;
      } else {
        const buffer = await this.workbookToBuffer(workbook);
        this.logger.log('Excel文件导出为Buffer成功');
        return buffer;
      }
    } catch (error) {
      this.logger.error('导出Excel文件失败', error);
      throw error;
    }
  }

  /**
   * 将工作簿导出为Buffer
   * @param workbook 工作簿实例
   * @returns Buffer数据
   */
  async workbookToBuffer(workbook: ExcelJS.Workbook): Promise<ExcelJS.Buffer> {
    this.logger.log('开始将工作簿转换为Buffer');

    try {
      const buffer = await workbook.xlsx.writeBuffer();
      this.logger.log(`工作簿转换为Buffer成功，大小: ${buffer.byteLength || 0} bytes`);
      return buffer;
    } catch (error) {
      this.logger.error('工作簿转换为Buffer失败', error);
      throw error;
    }
  }

  /**
   * 将工作簿写入文件
   * @param workbook 工作簿实例
   * @param filePath 文件路径
   */
  async writeToFile(workbook: ExcelJS.Workbook, filePath: string): Promise<void> {
    this.logger.log(`开始将工作簿写入文件: ${filePath}`);

    try {
      await workbook.xlsx.writeFile(filePath);
      this.logger.log(`工作簿成功写入文件: ${filePath}`);
    } catch (error) {
      this.logger.error(`工作簿写入文件失败: ${filePath}`, error);
      throw error;
    }
  }

  /**
   * 读取Excel文件
   * @param filePath 文件路径或Buffer
   * @returns 工作簿实例
   */
  async readWorkbook(filePath: string | ExcelJS.Buffer): Promise<ExcelJS.Workbook> {
    const fileType = typeof filePath === 'string' ? '文件路径' : 'Buffer';
    this.logger.log(`开始读取Excel文件 - 输入类型: ${fileType}`);

    try {
      const workbook = new ExcelJS.Workbook();

      if (typeof filePath === 'string') {
        await workbook.xlsx.readFile(filePath);
        this.logger.log(`Excel文件读取成功: ${filePath}`);
      } else {
        await workbook.xlsx.load(filePath);
        this.logger.log(`Buffer数据读取成功，大小: ${filePath.byteLength || 0} bytes`);
      }

      this.logger.debug(`工作簿包含 ${workbook.worksheets.length} 个工作表`);
      return workbook;
    } catch (error) {
      this.logger.error(`读取Excel文件失败 - 输入类型: ${fileType}`, error);
      throw error;
    }
  }

  /**
   * 创建多工作表工作簿
   * @description 创建包含多个工作表的工作簿
   * @param sheetConfigs 工作表配置数组
   * @returns 工作簿
   */
  async createMultiSheetWorkbook(
    sheetConfigs: SheetConfig[]
  ): Promise<ExcelJS.Workbook> {
    this.logger.log(`开始创建多工作表工作簿 - 工作表数量: ${sheetConfigs.length}`);

    try {
      const workbook = new ExcelJS.Workbook();
      this.logger.debug('多工作表工作簿实例创建成功');

      for (const config of sheetConfigs) {
        this.logger.debug(`开始添加工作表: ${config.name}`);
        const worksheet = workbook.addWorksheet(config.name);

        worksheet.columns = config.columns;
        this.logger.debug(`工作表 ${config.name} 列定义设置完成`);

        worksheet.addRows(config.data);
        this.logger.debug(`工作表 ${config.name} 数据行添加完成，共 ${config.data.length} 行`);
      }

      this.logger.log(`多工作表工作簿创建成功 - 总工作表数: ${workbook.worksheets.length}`);
      return workbook;
    } catch (error) {
      this.logger.error('创建多工作表工作簿失败', error);
      throw error;
    }
  }

  /**
   * 为已有工作簿添加工作表
   * @param workbook 工作簿实例
   * @param sheetName 工作表名称
   * @param columns 列定义
   * @param data 数据
   * @returns 添加的工作表
   */
  addWorksheet(
    workbook: ExcelJS.Workbook,
    sheetName: string,
    columns: Partial<ExcelJS.Column>[],
    data: any[]
  ): ExcelJS.Worksheet {
    this.logger.log(`开始向工作簿添加新工作表: ${sheetName}`);

    try {
      const worksheet = workbook.addWorksheet(sheetName);
      this.logger.debug(`工作表 "${sheetName}" 添加成功`);

      worksheet.columns = columns;
      this.logger.debug('列定义设置完成');

      worksheet.addRows(data);
      this.logger.debug(`数据行添加完成，共 ${data.length} 行`);

      this.logger.log(`工作表 ${sheetName} 添加完成`);
      return worksheet;
    } catch (error) {
      this.logger.error(`向工作簿添加工作表失败: ${sheetName}`, error);
      throw error;
    }
  }
}
